import { IWorld } from '../../engine/ecs/common/world.interface';
import { CameraComponent } from '../../engine/ecs/components/camera.component';
import { ColliderComponent } from '../../engine/ecs/components/collider.component';
import { LocationComponent } from '../../engine/ecs/components/location.component';
import { System } from '../../engine/ecs/systems/system';
import { MathUtils } from '../../engine/utilities/math-utils';
import { DefeatEventComponent } from '../components/defeat-event.component';
import { EnemyComponent } from '../components/enemy.component';
import { VictoryEventComponent } from '../components/victory-event.component';

import { camera } from '../entities/camera.entity';

export class GameCoreSystem extends System {
  #isEventPushed = false;

  onUpdate(world: IWorld, dt: number) {
    if (this.#isEventPushed) {
      return;
    }

    const enemyEntities = world.findEntitiesByComponent(EnemyComponent);
    if (enemyEntities.length === 0) {
      this.pushVictoryEvent(world);
      return;
    }

    for (const enemy of enemyEntities) {
      const cameraLocationComponent = world.findComponentByEntity(camera, LocationComponent)!;
      const cameraComponent = world.findComponentByEntity(camera, CameraComponent)!;
      const enemyLocationComponent = world.findComponentByEntity(enemy, LocationComponent)!;
      const enemyColliderComponent = world.findComponentByEntity(enemy, ColliderComponent)!;

      const cameraLocation = cameraLocationComponent.location;
      const cameraDimension = cameraComponent.viewDimension;

      const enemyIsOutOfCamera = enemyColliderComponent.configs.every((config) => {
        const enemyLocation = enemyLocationComponent.location.add(config.relativeLocation);
        const enemyDimension = config.dimension;
        return !MathUtils.isIntersection(enemyLocation, enemyDimension, cameraLocation, cameraDimension);
      });

      if (enemyIsOutOfCamera) {
        this.pushDefeatEvent(world);
        return;
      }
    }
  }

  private pushVictoryEvent(world: IWorld) {
    if (this.#isEventPushed) {
      return;
    }

    this.#isEventPushed = true;
    world.pushEvent(VictoryEventComponent, new VictoryEventComponent());
  }

  private pushDefeatEvent(world: IWorld) {
    if (this.#isEventPushed) {
      return;
    }

    this.#isEventPushed = true;
    world.pushEvent(DefeatEventComponent, new DefeatEventComponent());
  }
}
